package com.py.custom.tms.application.jvm.classload;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * 自定义ClassLoader
 */
public class MyTest16 extends ClassLoader {

    private final String fileExtension = ".class";

    private String classLoaderName;

    private String path;

    public void setPath(String path) {
        this.path = path;
    }

    public MyTest16(String classLoaderName) {
        // 父类加载器，默认是应用类加载器(AppClassLoad)
        super();
        this.classLoaderName = classLoaderName;
    }

    public MyTest16(ClassLoader parent) {
        // 指定父类加载器；提供给 java.system.class.loader指定的参数调用
        super(parent);
    }

    public MyTest16(ClassLoader parent, String classLoaderName) {
        // 指定父类加载器
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    @Override
    public String toString() {
        return "[" + this.classLoaderName + "]";
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("findClass invoked: " + name);
        System.out.println("class loader name: " + classLoaderName);
        byte[] data = loadClassData(name);
        return defineClass(name, data, 0, data.length);
    }

    private byte[] loadClassData(String name) {
        byte[] data = null;
        String absolutePath = path + name.replace(".", "/") + fileExtension;
        try (InputStream is = new FileInputStream(absolutePath);
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            int len = 0;
            while (-1 != (len = is.read())) {
                baos.write(len);
            }
            data = baos.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return data;
    }

    public static void main(String[] args) throws Exception {
        //前提：删除idea中编译好的MyTest1.class；把MyTest1.class复制到指定路径
        MyTest16 loader1 = new MyTest16("loader1");
        loader1.setPath("/Users/pengyou03/Desktop/");
        Class<?> clazz1 = loader1.loadClass("com.py.custom.tms.application.jvm.classload.MyTest1");
        Object object1 = clazz1.newInstance();
        System.out.println(clazz1.hashCode());
        System.out.println(object1);

        System.out.println("-------");
        System.out.println();

        MyTest16 loader2 = new MyTest16("loader2");
        loader2.setPath("/Users/pengyou03/Desktop/");
        Class<?> clazz2 = loader2.loadClass("com.py.custom.tms.application.jvm.classload.MyTest1");
        Object object2 = clazz2.newInstance();
        System.out.println(clazz2.hashCode());
        System.out.println(object2);

        System.out.println("-------");
        System.out.println();

        //指定父加载器是loader1；当loader3加载类的时候，会委托loader1进行加载；loader1可以加载
        MyTest16 loader3 = new MyTest16(loader1, "loader3");
        loader3.setPath("/Users/pengyou03/Desktop/");
        Class<?> clazz3 = loader3.loadClass("com.py.custom.tms.application.jvm.classload.MyTest1");
        Object object3 = clazz3.newInstance();
        System.out.println(clazz3.hashCode());
        System.out.println(object3);

        System.out.println("-------");
        System.out.println();

        //类的卸载(Class)：-XX:+TraceClassUnLoading
        MyTest16 loader = new MyTest16("loader1");
        loader.setPath("/Users/pengyou03/Desktop/");
        Class<?> clazz = loader.loadClass("com.py.custom.tms.application.jvm.classload.MyTest1");
        Object object = clazz.newInstance();
        System.out.println(clazz.hashCode());
        System.out.println(object);
        // 卸载的条件：类加载器没有引用、类对象没有引用、实例对象没有引用
        loader = null;
        clazz = null;
        object = null;
        System.gc();
        // [Unloading class com.py.custom.tms.application.jvm.classload.MyTest1 0x00000007c0062028]

    }

}
